cmake_minimum_required(VERSION 3.10)
project(OpenCL_Tests)

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 设置可执行文件输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)

# 查找OpenCL包
find_package(OpenCL REQUIRED)

# 避免 cl_version.h 打印提示信息（会产生编译器 note）
# 定义目标 OpenCL 版本为 220（与系统 OpenCL 2.2 匹配）
add_compile_definitions(CL_TARGET_OPENCL_VERSION=220)

# 针对常见编译器抑制部分噪声警告（可按需调整）
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    # 禁用弃用声明警告，和一些未使用参数/变量的警告（可按需删减）
    add_compile_options(-Wno-deprecated-declarations -Wno-unused-parameter -Wno-unused-variable)
elseif(MSVC)
    # MSVC: 4996 是常见的弃用警告，按需添加更多 /wdNNNN
    add_compile_options(/wd4996)
endif()

# 设置源文件目录
set(KERNELS_DIR "kernels")
set(CASE_DIR "case")

# 获取所有.cl文件和对应的.cpp文件
file(GLOB CL_FILES "${KERNELS_DIR}/*.cl")
file(GLOB CPP_TEST_FILES "${CASE_DIR}/*.cpp")

# 创建bin目录（确保目录存在）
file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

# 单独的复制目标：只执行一次复制全部 kernels 到输出目录，避免并行构建时多个目标同时复制导致的错误
add_custom_target(copy_kernels
    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${KERNELS_DIR}
    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/${KERNELS_DIR} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${KERNELS_DIR}
    COMMENT "Copying kernel files to ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${KERNELS_DIR}"
)

# 为每个测试程序创建可执行文件
foreach(cpp_file ${CPP_TEST_FILES})
    # 获取测试程序的基本名称
    get_filename_component(test_name ${cpp_file} NAME_WE)
    
    # 查找对应的.cl文件（假设同名）
    set(corresponding_cl_file "${CMAKE_CURRENT_SOURCE_DIR}/${KERNELS_DIR}/${test_name}.cl")
    
    # 检查对应的.cl文件是否存在
    if(EXISTS ${corresponding_cl_file})
        message(STATUS "Found matching kernel for ${test_name}: ${corresponding_cl_file}")
        
        # 创建可执行文件，将.cl文件作为依赖
        add_executable(${test_name} ${cpp_file} ${corresponding_cl_file})
        
        # 将.cl文件标记为资源文件，避免编译器处理
        set_source_files_properties(${corresponding_cl_file} PROPERTIES HEADER_FILE_ONLY TRUE)
    else()
        # 如果没有对应的.cl文件，只编译C++文件
        # 将 WARNING 降为 STATUS，避免在每次构建时将缺少 kernel 报为警告
        message(STATUS "No matching kernel file found for ${test_name}")
        add_executable(${test_name} ${cpp_file})
    endif()
    
    # 链接OpenCL库
    target_link_libraries(${test_name} ${OpenCL_LIBRARIES})
    
    # 添加包含目录
    target_include_directories(${test_name} PRIVATE 
        ${OpenCL_INCLUDE_DIRS}
        ${CMAKE_CURRENT_SOURCE_DIR})
    
    # 添加编译定义，传递kernels目录路径
    target_compile_definitions(${test_name} PRIVATE 
        KERNELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/${KERNELS_DIR}")
    
    # 确保在构建可执行文件前已复制 kernels（避免并发复制冲突）
    add_dependencies(${test_name} copy_kernels)
    
    # 收集所有测试目标名称
    list(APPEND ALL_TEST_TARGETS ${test_name})
        
endforeach()

# 可选：创建一个构建所有测试的目标
add_custom_target(build_all_tests DEPENDS ${ALL_TEST_TARGETS})

# 显示总结信息
list(LENGTH CL_FILES CL_FILES_COUNT)
list(LENGTH CPP_TEST_FILES CPP_TEST_FILES_COUNT)
message(STATUS "Configuration complete:")
message(STATUS "  - Found ${CL_FILES_COUNT} kernel files")
message(STATUS "  - Found ${CPP_TEST_FILES_COUNT} test files")
message(STATUS "  - Executables will be output to: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")